package com.stars.easyms.security.jwt;

import com.stars.easyms.security.properties.EasyMsSecurityProperties;
import io.jsonwebtoken.*;
import io.jsonwebtoken.io.Decoders;
import io.jsonwebtoken.security.Keys;
import lombok.extern.slf4j.Slf4j;
import org.springframework.lang.Nullable;

import java.nio.charset.StandardCharsets;
import java.security.Key;
import java.util.*;

/**
 * <p>interfaceName: EasyMsJWTProvider</p>
 * <p>description: jwt提供者</p>
 *
 * @author guoguifang
 * @version 1.6.1
 * @date 2020-08-18 10:10
 */
@Slf4j
public class EasyMsJWTProvider {

    private Key key;

    private long tokenValidityInMilliseconds;

    private long tokenValidityInMillisecondsForRememberMe;

    private final EasyMsSecurityProperties easyMsSecurityProperties;

    public EasyMsJWTProvider(EasyMsSecurityProperties easyMsSecurityProperties) {
        this.easyMsSecurityProperties = easyMsSecurityProperties;
    }

    public void init() {
        byte[] keyBytes;
        String secret = easyMsSecurityProperties.getJwt().getSecret();
        if (!StringUtils.isEmpty(secret)) {
            log.warn("Warning: the JWT key used is not Base64-encoded. We recommend using the `{}.jwt.base64-secret` key for optimum security.",
                    EasyMsSecurityProperties.EASYMS_SECURITY_PREFIX);
            keyBytes = secret.getBytes(StandardCharsets.UTF_8);
        } else {
            if (log.isDebugEnabled()) {
                log.debug("Using a Base64-encoded JWT secret key");
            }
            keyBytes = Decoders.BASE64.decode(easyMsSecurityProperties.getJwt().getBase64Secret());
        }
        this.key = Keys.hmacShaKeyFor(keyBytes);
        this.tokenValidityInMilliseconds = 1000 * easyMsSecurityProperties.getJwt().getTokenValidityInSeconds();
        this.tokenValidityInMillisecondsForRememberMe = 1000 * easyMsSecurityProperties.getJwt().getTokenValidityInSecondsForRememberMe();
    }

    public String createToken(UserInfo userInfo, boolean rememberMe) {

        // header：指定类型
        Map<String, Object> header = new HashMap<>(2);
        header.put(Header.TYPE, Header.JWT_TYPE);

        // 发行时间、过期时间
        Date iat = new Date();
        Date expireDate;
        if (rememberMe) {
            expireDate = new Date(iat.getTime() + this.tokenValidityInMillisecondsForRememberMe);
        } else {
            expireDate = new Date(iat.getTime() + this.tokenValidityInMilliseconds);
        }

        return Jwts
                .builder()
                .setHeader(header)
                .setIssuer(easyMsSecurityProperties.getJwt().getIssuer())
                .setClaims(userInfo.toClaims())
                .signWith(key, SignatureAlgorithm.HS512)
                .setIssuedAt(iat)
                .setExpiration(expireDate)
                .compact();
    }

    @Nullable
    public UserInfo getAuthentication(String token) {
        try {
            Claims claims = Jwts.parserBuilder().setSigningKey(key).build().parseClaimsJws(token).getBody();
            return UserInfo.parseClaims(claims);
        } catch (JwtException | IllegalArgumentException e) {
            log.info("Invalid JWT token.");
            log.trace("Invalid JWT token trace.", e);
        }
        return null;
    }

}
